﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;

using System.IO;
using PdfSharp;
using PdfSharp.Pdf;
using PdfSharp.Drawing;
using PdfSharp.Drawing.Layout;

using HtmlTo.LayoutEngine;

namespace HtmlTo.Pdf
{
    /// <summary>
    /// <para>Converter class</para>
    /// </summary>
    public class Converter
    {
        public Converter()
        {
            this.PageSize = PageSize.A4;
        }

        public PageSize PageSize { get; set; }

        private delegate void Draw(LayoutNode parent);

        /// <summary>
        /// <para>Convert document to PDF</para>
        /// </summary>
        /// <param name="layoutDoc">A layoutEngine document</param>
        /// <returns>PDF in a byte array</returns>
        public byte[] ToBytes(LayoutDocument layoutDoc)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                PdfDocument doc = new PdfDocument(stream);
                doc.Info.Title = layoutDoc.Title;

                PdfPage page;
                List<double> pages = new List<double>();
                XSize pageSize = PageSizeConverter.ToSize(this.PageSize);
                float scale = (float)pageSize.Width / layoutDoc.Bounds.Width;
                try
                {
                    // draw a node (recursive)
                    Draw draw = null;
                    draw = node =>
                    {
                        // get the page
                        int pageIndex =(int)Math.Floor((node.Bounds.Top)*scale / pageSize.Height);
                        if (doc.PageCount <= pageIndex)
                        {
                            page = doc.AddPage();
                            pages.Add(pageSize.Height);
                        }
                        else
                        {
                            page = doc.Pages[pageIndex];
                        }
                        double pageTop = pages.Take(pageIndex).Sum();
                        XGraphics gfx = XGraphics.FromPdfPage(page);

                        // get the bounds of the node
                        XRect bounds = new Func<XRect>(() =>
                        {
                            Point loc = node.Bounds.Location;
                            return new XRect(
                                loc.X * scale, loc.Y * scale - pageTop,
                                node.Bounds.Width * scale, node.Bounds.Height * scale);
                        })();

                        if (node is LayoutText)
                        {
                            LayoutText text = node as LayoutText;
                            XTextFormatter textFormatter = new XTextFormatter(gfx);
                            textFormatter.Alignment = XParagraphAlignment.Left;

                            XFontStyle fontStyle = XFontStyle.Regular;
                            if (text.IsBold && text.IsItalic) { fontStyle = XFontStyle.BoldItalic; }
                            else if (text.IsBold) { fontStyle = XFontStyle.Bold; }
                            else if (text.IsItalic) { fontStyle = XFontStyle.Italic; }
                            if (text.IsUnderline) { fontStyle = fontStyle | XFontStyle.Underline; }
                            if (text.IsStrikeout) { fontStyle = fontStyle | XFontStyle.Strikeout; }

                            XFont font = new XFont(text.FontFamily, text.FontSize * scale, fontStyle);

                            if (text.IsBold)
                            {
                                XGraphicsState state = gfx.Save();

                                // transform to fit
                                gfx.ScaleTransform(.9, 1);
                                bounds.X = bounds.X * 1.1;
                                bounds.Width = bounds.Width * 1.1;

                                textFormatter.DrawString(text.Value, font, XBrushes.Black, bounds, XStringFormats.TopLeft);

                                gfx.Restore(state);
                            }
                            else
                            {
                                textFormatter.DrawString(text.Value, font, XBrushes.Black, bounds, XStringFormats.TopLeft);
                            }
                        }
                        else
                        {
                            LayoutElement element = node as LayoutElement;
                            
                            if (element.BackgroundColor != Color.Transparent)
                            {
                                gfx.DrawRectangle(new XSolidBrush(element.BackgroundColor), bounds);
                            }

                            // draw bounding box
                            gfx.DrawRectangle(XPens.Aqua, bounds);
                        }

                        gfx.Dispose();

                        foreach (LayoutNode child in node.Children)
                        {
                            draw(child);
                        }
                    };

                    foreach (LayoutNode child in layoutDoc.Children)
                    {
                        draw(child);
                    }

                }
                finally
                {
                    doc.Close();
                }

                return stream.ToArray();
            }
        }

    }
}
